package org.alwayssuper.netmc.handler;

import org.alwayssuper.netmc.codec.Delimiter;
import org.alwayssuper.netmc.util.ByteBufUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.util.internal.ObjectUtil;

import java.util.List;

import static io.netty.util.internal.ObjectUtil.checkPositive;

/**
 * 处理类似GBT32960这种只有头部存在分隔符的报文类型
 * 
 * @author alwayssuper
 */
public class HeaderDelimiterBasedFrameDecoder extends ByteToMessageDecoder {

    private final Delimiter[] delimiters;
    private final int maxFrameLength;
    private final boolean failFast;
    private boolean discardingTooLongFrame;
    private int tooLongFrameLength;

    public HeaderDelimiterBasedFrameDecoder(int maxFrameLength, Delimiter... delimiters) {
        this(maxFrameLength, true, delimiters);
    }

    public HeaderDelimiterBasedFrameDecoder(int maxFrameLength, boolean failFast, Delimiter... delimiters) {
        validateMaxFrameLength(maxFrameLength);
        ObjectUtil.checkNonEmpty(delimiters, "delimiters");

        this.delimiters = delimiters;
        this.maxFrameLength = maxFrameLength;
        this.failFast = failFast;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) {
        // 尝试所有分隔符并选择产生最短帧的分隔符
        int minFrameLength = Integer.MAX_VALUE;
        Delimiter minDelim = null;
        
        for (Delimiter delim : delimiters) {
            int frameLength = ByteBufUtils.indexOf(buffer, delim.value);
            if (frameLength >= 0 && frameLength < minFrameLength) {
                minFrameLength = frameLength;
                minDelim = delim;
            }
        }

        if (minDelim != null) {
            return decodeWithDelimiter(buffer, minDelim, minFrameLength);
        } else {
            // 未找到分隔符
            handleNoDelimiterFound(buffer);
            return null;
        }
    }

    private Object decodeWithDelimiter(ByteBuf buffer, Delimiter minDelim, int minFrameLength) {
        int minDelimLength = minDelim.value.length;

        if (discardingTooLongFrame) {
            // 刚刚完成丢弃一个非常大的帧
            // 回到初始状态
            discardingTooLongFrame = false;
            buffer.skipBytes(minFrameLength + minDelimLength);

            int tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            if (!failFast) {
                fail(tooLongFrameLength);
            }
            return null;
        }

        if (minFrameLength > maxFrameLength) {
            // 丢弃读取的帧
            buffer.skipBytes(minFrameLength + minDelimLength);
            fail(minFrameLength);
            return null;
        }

        // 对于头部分隔符，我们从缓冲区的开始读取到分隔符
        if (minDelim.strip) {
            return decodeWithStrip(buffer, minDelim, minFrameLength, minDelimLength);
        } else {
            return decodeWithoutStrip(buffer, minDelim, minFrameLength, minDelimLength);
        }
    }

    private ByteBuf decodeWithStrip(ByteBuf buffer, Delimiter minDelim, int minFrameLength, int minDelimLength) {
        // 忽略长度为0的帧
        if (minFrameLength != 0) {
            ByteBuf frame = buffer.readRetainedSlice(minFrameLength);
            buffer.skipBytes(minDelimLength);
            return frame;
        } else {
            // 处理分隔符位于缓冲区开头的情况
            // 这是为了满足要求：可正确识别只有一条头部有标识符且尾部无标识符的报文
            // 和可正确处理头部有标识符且尾部无标识符的报文的黏包情况
            
            // 分隔符在开头，所以我们需要先跳过它
            buffer.skipBytes(minDelimLength);
            
            // 现在查找下一个分隔符以处理黏包
            int searchStartIndex = buffer.readerIndex();
            int searchLength = buffer.readableBytes();
            
            if (searchLength > 0) {
                // 创建缓冲区的一个切片用于搜索
                ByteBuf searchBuffer = buffer.slice(searchStartIndex, searchLength);
                // 在搜索缓冲区中查找下一个分隔符
                int nextDelimiterIndex = ByteBufUtils.indexOf(searchBuffer, minDelim.value);
                
                if (nextDelimiterIndex >= 0) {
                    // 如果找到另一个分隔符，则读取到该点的帧
                    return buffer.readRetainedSlice(nextDelimiterIndex);
                } else {
                    // 如果没有找到其他分隔符，则读取缓冲区的其余部分
                    // 这处理了只有一个帧的情况
                    return buffer.readRetainedSlice(searchLength);
                }
            }
            
            return null;
        }
    }

    private ByteBuf decodeWithoutStrip(ByteBuf buffer, Delimiter minDelim, int minFrameLength, int minDelimLength) {
        // 在帧中包含分隔符
        if (minFrameLength != 0) {
            return buffer.readRetainedSlice(minFrameLength + minDelimLength);
        } else {
            // 处理分隔符位于缓冲区开头的情况，且需要保留分隔符
            // 查找下一个分隔符以处理黏包
            int searchStartIndex = buffer.readerIndex() + minDelimLength;
            int searchLength = buffer.readableBytes() - minDelimLength;
            
            if (searchLength > 0) {
                // 创建缓冲区的一个切片用于搜索
                ByteBuf searchBuffer = buffer.slice(searchStartIndex, searchLength);
                // 在搜索缓冲区中查找下一个分隔符
                int nextDelimiterIndex = ByteBufUtils.indexOf(searchBuffer, minDelim.value);
                
                if (nextDelimiterIndex >= 0) {
                    // 如果找到另一个分隔符，则读取到该点的帧（包含第一个分隔符）
                    return buffer.readRetainedSlice(nextDelimiterIndex + minDelimLength);
                } else {
                    // 如果没有找到其他分隔符，则读取缓冲区的其余部分（包含第一个分隔符）
                    return buffer.readRetainedSlice(buffer.readableBytes());
                }
            } else if (searchLength == 0) {
                // 如果缓冲区中没有更多数据，则只读取分隔符
                return buffer.readRetainedSlice(minDelimLength);
            }
            
            return null;
        }
    }

    private void handleNoDelimiterFound(ByteBuf buffer) {
        if (!discardingTooLongFrame) {
            if (buffer.readableBytes() > maxFrameLength) {
                // 丢弃缓冲区的内容
                tooLongFrameLength = buffer.readableBytes();
                buffer.skipBytes(buffer.readableBytes());
                discardingTooLongFrame = true;
                if (failFast) {
                    fail(tooLongFrameLength);
                }
            }
        } else {
            // 由于未找到分隔符，仍在丢弃缓冲区
            tooLongFrameLength += buffer.readableBytes();
            buffer.skipBytes(buffer.readableBytes());
        }
    }

    private void fail(long frameLength) {
        if (frameLength > 0) {
            throw new TooLongFrameException("frame length exceeds " + maxFrameLength + ": " + frameLength + " - discarded");
        } else {
            throw new TooLongFrameException("frame length exceeds " + maxFrameLength + " - discarding");
        }
    }

    private static void validateMaxFrameLength(int maxFrameLength) {
        checkPositive(maxFrameLength, "maxFrameLength");
    }
}